

class NodeMenu < Widget

  def initialize options = {}
    @options = options.stringify_keys
    super()
    @root = multiple_item_wrapper self
  end

  def nodes
    if !@nodes
      @nodes = {}
      NodeThing.all.each { |node|
        (@nodes[node.parent_identity] ||= []) << node
      }
    end
    @nodes
  end

  def self.each options = {}, &block
    new(options).tap { |menu| menu.each &block }
  end

  def self.each_node options = {}, &block
    new(options).tap { |menu| menu.each &block }
  end

  def single_item_wrapper parent, info
    Element.new(:li, :class => 'level-'+info[:level].to_s).tap { |li|
      parent << li
    }
  end

  def multiple_item_wrapper parent = nil
    Element.new(:ul).tap { |ul|
      parent << ul if parent
    }
  end

  def roots
    nodes[nil]
  end

  def each &block
    # limit the number of items in menu
    # helps very crazy stuff happens
    i = 100

    # looks ulgy but its WAY more fault-tolerant
    # than simple recursion

    places = {}
    #p nodes[nil]
    unprocessed = nodes[nil]
    while unprocessed.length > 0 && (i -= 1) > 0
      r = unprocessed.shift
      next if r.nil? #hot fix
      
      places[r.identity] = info = { }

      if parent = places[r.parent_identity]
        next if (parent[:level]+1) >= max_level
        parent[:ul] ||= multiple_item_wrapper parent[:li]
        info[:level] = parent[:level]+1
        info[:li] = single_item_wrapper(parent[:ul], info)
      elsif r.is_root
        info[:level] = 0
        info[:li] = single_item_wrapper(@root, info)
      else
        raise "#{r.title} is not a root, nor has parent. (identity #{r.identity})"
      end


      # call user callback
      block.call r, info[:li]      

      unprocessed.push(*(nodes[r.identity] || []))
    end

    self
  end


  def max_level
    @options['depth'] || 10
  end

  def each_node
    roots().each { |root| each_node_in(root, 1, &block) }
  end

  def each_node_in node, level, &block
    block.call node, level
    nodes.select { |n| 
      n.parent_identity == node.identity
    }.each { |child|
      each_node_in child, (level+1), &block
    }
  end

  class Test < AppTest
    def test_node_menu_does_not_iterate_when_no_nodes_are_there
      #empty repo
      DataMapper.setup(:test, :adapter => 'in_memory')
      repository(:test) {
        called = 0
        NodeMenu.each { |*a| called += 1 }
        eq called, 0
      }
    end
  end
end